import LocalStorage from "../../framework/utils/LocalStorage";
import {wxApi} from "../api/WxApi";
import {HttpResponse} from "./HttpResponse";
import Common from "../../framework/utils/Common";
import {getDefaultConfig, onConfigSave} from "./ConfigHelper";

const DEBUG: boolean = false;

export default class Config {

    protected configName: string = null;

    protected ts: number = 0;

    // 标记当前正在清理配置数据,用于阻止清除过程中又被写入新数据.
    protected clearing: boolean = false;

    constructor(configName: string) {
        this.configName = configName;
    }

    public init() {
        LocalStorage.getObj("user_profile_" + this.configName, this);
    }

    public getConfigName(): string {
        return this.configName;
    }

    /**
     * 在云同步完成后调用.
     * @param success
     * @param oldData 同步前的原数据.有时需要跟旧数据做对比.
     * @param upload
     */
    public onSynced(success: boolean, oldData: any, upload: boolean) {
    }

    /**
     * 由于数据同步依赖于登录业务,因此无法在启动时就开始同步, 需要登录成功后手动通知同步.
     * 如果未通知同步,则仅维护本地数据.
     */
    public syncFromNet() {
        if (DEBUG) {
            return;
        }
        if (this.clearing) {
            cc.log("需要清理数据:", this.configName);
            return;
        }
        wxApi.getUserDataSafe(this.configName).then(resp => {
            if (this.clearing) {
                cc.log("需要清理数据:", this.configName);
                return;
            }
            let upload: boolean = false;
            const oldData = {};
            Common.assign(oldData, this);
            if (HttpResponse.isOk(resp) && resp.data && resp.data[this.configName]) {
                const data = resp.data[this.configName];
                const ts = data.ts || 0;
                if (this.ts <= ts) {

                    let defConfg = getDefaultConfig(this);
                    // 时间一致的情况下,以服务器数据为准. 这样可以配合服务器控制重写本地数据.防止本地数据坏掉却又无法处理的状态.
                    Common.deepAssign(true, defConfg, resp.data[this.getConfigName()]);
                    // 先用默认对象接收服务器数据.防止本地数据深拷贝时残留.
                    Common.assign(this, defConfg);

                    LocalStorage.setObj("user_profile_" + this.configName, this);
                    cc.log(this.configName, "从网络同步数据成功.");
                } else {
                    upload = true;
                    // 这里不能调用save(),会导致ts 记数不正确.
                    this.upload();
                    cc.log(this.configName, "向服务器同步数据.");
                }
            }
            this.onSynced(HttpResponse.isOk(resp), oldData, upload);
        }).catch((reason => {
            this.onSynced(false, this, false);
            cc.log("网络同步失败:", reason);
        }))
    }

    public save() {
        if (this.clearing) {
            cc.log("需要清理数据:", this.configName);
            return;
        }

        this.ts = cc.sys.now();

        // 浏览器环境直接保存,否则进行暂存.
        if (cc.sys.isBrowser) {
            cc.log("浏览器环境,直接保存数据.");
            this.saveReal();
        } else {
            onConfigSave(this);
            this.upload();
        }
    }

    // 保存到本地,同时提交上传.
    public saveReal() {
        if (this.clearing) {
            cc.log("需要清理数据:", this.configName);
            return;
        }
        this.upload();
        LocalStorage.setObj("user_profile_" + this.configName, this);
    }

    public clear(): Promise<any> {

        // 不能深拷贝.
        Common.assign(this, getDefaultConfig(this));
        LocalStorage.setObj("user_profile_" + this.configName, this);
        console.log(this.configName, "正在清除配置信息...");
        this.clearing = true;
        if (!wxApi.isLogined()) {
            cc.log("未登录,将在登录成功后第一次同步网络时进行清除.");
            return;
        }
        return wxApi.deleteUserData(this.configName).then(resp2 => {
            if (HttpResponse.isOk(resp2)) {
                cc.log("数据清除成功.", this.configName)
            } else {
                cc.log("数据清除失败.", this.configName, resp2)
            }
            this.clearing = false;
        }).catch((error) => {
            this.clearing = false;
        });
    }

    private upload() {
        if (CC_DEBUG) {
            return;
        }
        const data = {};
        data[this.configName] = this;
        wxApi.setUserData(data).then(resp2 => {
            if (HttpResponse.isOk(resp2)) {
                cc.log("数据更新成功.", this.configName)
            }
        });
    }

}